﻿// ———————————————————————–
// <copyright file="CoreRoutingService.cs" company="The MITRE Corporation">
//    Copyright (c) 2010 The MITRE Corporation. All rights reserved.
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// </copyright>
// ———————————————————————–
/////////////////////////////////////////////////////////////////////////////////////////////////
// UDPListenerService.cs - "Creates and Run a UDP Listener for DE POSTs over UDP."
// Project: "IC.NETSOA"- "ICNETUDPService"
//
// Language:    C#, .NET 4.0
// Platform:    Windows 7, VS 2010
// Author:      Greg Kern The MITRE Corporation
/////////////////////////////////////////////////////////////////////////////////////////////////

// Copyright (c) 2012 The MITRE Corporation. All rights reserved.
//
// NOTICE
// This software was produced for the U. S. Government
// under Contract No. FA8721-11-C-0001, and is
// subject to the Rights in Noncommercial Computer Software
// and Noncommercial Computer Software Documentation Clause
// (DFARS) 252.227-7014 (JUN 1995)

using System;
using System.Configuration;
using System.IO;
using System.Net;
using System.ServiceProcess;
using System.Text;
using Donuts.Net;
using Donuts.Net.Utils;

namespace ICNETUDPService
{
  /// <summary>
  /// Creates and Run a UDP Listener for DE POSTs over UDP.
  /// </summary>
  public class UDPListenerService : ServiceBase
  {
    private Udp endPoint;
    private string dePostURL;

    /// <summary>
    /// Initializes a new instance of the <see cref="UDPListenerService"/> class.
    /// </summary>
    public UDPListenerService()
    {
      // Retrieve the DE POST URL from the Application Configuration file. 
      this.dePostURL = ConfigurationManager.ConnectionStrings["DEPostURL"].
        ConnectionString;
      // Set-up the UDP Listener and the Data Recieved handler.
      this.endPoint = new Udp(null, new Url(ConfigurationManager.
        ConnectionStrings["UDP"].ConnectionString));
      this.endPoint.DataReceived += ProcessData;
    }

    /// <summary>
    /// Mains this instance.
    /// </summary>
    public static void Main()
    {
      ServiceBase.Run(new UDPListenerService());
    }

    /// <summary>
    /// When implemented in a derived class, executes when a Start command is
    /// sent to the service by the Service Control Manager (SCM) or when the
    /// operating system starts (for a service that starts automatically). 
    /// Specifies actions to take when the service starts.
    /// </summary>
    /// <param name="args">Data passed by the start command.</param>
    protected override void OnStart(string[] args)
    {
      this.endPoint.Start();
    }

    /// <summary>
    /// When implemented in a derived class, executes when a Stop command is 
    /// sent to the service by the Service Control Manager (SCM). Specifies
    /// actions to take when a service stops running.
    /// </summary>
    protected override void OnStop()
    {
      this.endPoint.Stop();
    }

    /// <summary>
    /// Handler to POST the messages to the DE POST RESTful service as they arrive.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing 
    /// the event data.</param>
    private void ProcessData(object sender, EventArgs e)
    {
      string completeMessage;
      int messageLength;
      string returnURL = string.Empty;
      int returnPort = int.MinValue;
      HttpStatusCode statusCode;

      try
      {
        Byte[] data = ((Udp)sender).Recv();
        completeMessage = Encoding.UTF8.GetString(data);
        
        // Split the message into it part to pull the header data and 
        // EDXL message.
        string[] messageParts = completeMessage.Split('\t');

        // If there are atleast 2 part to the message the first part is the
        // length of the complete message.
        if (messageParts.Length > 1)
        {
          int.TryParse(messageParts[0], out messageLength);
        }
        // If there are atleast 4 part to the message the second and third
        // parts are the return URL and port number, respectfully, to send
        // the response of the POST to.
        if (messageParts.Length > 3)
        {
          returnURL = messageParts[1];
          int.TryParse(messageParts[2], out returnPort);
        }

        // With or without the header data the EDXL message will be the last
        // part of the complete message.
        string edxlMessage = messageParts[messageParts.Length - 1];

        // POST message to the DE POST RESTful service.
        HttpWebRequest request = null;
        UTF8Encoding encoding = new UTF8Encoding();
        byte[] bytes = encoding.GetBytes(edxlMessage);
        Uri uri = new Uri(this.dePostURL);
        request = (HttpWebRequest)WebRequest.Create(uri);
        request.Method = "POST";
        request.ContentType = "text/xml";
        request.ContentLength = bytes.Length;
        using (Stream writeStream = request.GetRequestStream())
        {
          writeStream.Write(bytes, 0, bytes.Length);
        }

        // get status code
        using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
        {
          statusCode = response.StatusCode;
        }
      }
      catch
      {
        // If anything goes wrong set the status code to
        // "Internal Server Error", 500.
        statusCode = HttpStatusCode.InternalServerError;
      }

      // If the return data was sent in the header then send the status code as
      // a response to the provided location.
      if (returnURL != string.Empty && returnPort != int.MinValue)
      {
        // Set-up UDP sender with provided return data.
        Udp returnSender = new Udp(new Url("udp", returnURL, returnPort), null);
        try
        {
          // Start the UDP sender.
          returnSender.Start();
          returnSender.DataSent += delegate
          {
            // Once the response is sent stop the UDP sender.
            returnSender.Stop();
          };

          // Encode and send the status code.
          UTF8Encoding encoding = new UTF8Encoding();
          byte[] bytes = encoding.GetBytes(((int)statusCode).ToString());
          returnSender.Send(bytes);
        }
        catch
        {
          // If something goes wrong no return message is returned, but stop
          // the UDP sender.
          returnSender.Stop();
        }
      }
    }
  }
}